using System;
using System.Collections.Generic;
using System.Reflection;

using TrueCode.DependencyManager.BuildActions.Injection;


namespace TrueCode.DependencyManager.Services.Parameters
{
  public class PositionalParameterProvider : IParameterProvider
  {
    private readonly List< PositionalParameter > _parameters;

    public PositionalParameterProvider( params PositionalParameter[] parameters )
    {
      _parameters = new List< PositionalParameter >( parameters );
    }

    public object[] GetValuesFor( IBuildSession context, ParameterInfo[] parameterInfo )
    {
      _parameters.Sort( ( l, r ) => l.Position.CompareTo( r.Position ) );

      #region Assert that position of parameters is in range of actural parameters count
      if( _parameters[ _parameters.Count - 1 ].Position >= parameterInfo.Length )
      {
        var exc = new InvalidOperationException( "Position of parameter exceed actual parameters count" );
        exc.Data.Add( "Parameter", _parameters[ _parameters.Count - 1 ] );
        exc.Data.Add( "Actual parameters count", parameterInfo.Length );
        throw exc;
      }
      #endregion

      var values = new object[parameterInfo.Length];
      var enumerator = _parameters.GetEnumerator();
      enumerator.MoveNext();

      for( int i = 0; i < values.Length; i++ )
      {
        if( i < enumerator.Current.Position )
          values[ i ] = context.MakeCopy( parameterInfo[ i ].ParameterType ).Build();
        else
        {
          #region Assert that actual parameter can be assigned from specified value
          if( ! parameterInfo[ i ].ParameterType.IsAssignableFrom( enumerator.Current.Value.GetType() ) )
          {
            var exc = new InvalidOperationException( "Actual parameter is not assignable from specified value" );
            exc.Data.Add( "Actual parameter info", parameterInfo[ i ] );
            exc.Data.Add( "Specified parameter", enumerator.Current );
            throw exc;
          }
          #endregion

          values[ i ] = enumerator.Current.Value;
          enumerator.MoveNext();
        }
      }
      return values;
    }
  }

  [Serializable]
  public class PositionalParameter
  {
    public readonly int Position;
    public readonly object Value;

    public PositionalParameter( int position, object value )
    {
      Position = position;
      Value = value;
    }
  }
}